

class DEvent {
    private _typeMap = {};
    private _idFactory = 0;
    private _maxId = Number.MAX_VALUE - 100;
    private _idMap = {};

    private genId(map):number{
        while(true){
            if(!map[this._idFactory ++]){
                return this._idFactory;
            }else{
                if(this._idFactory >= this._maxId){
                    this._idFactory = 0;
                }
            }
        }
    }

    public attach(type, fn:Function, obj:any):number{
        let idMap = this._typeMap[type];
        if(!idMap){
            idMap = {};
            idMap = this._typeMap[type] = {};
        }
        let id = this.genId(idMap);
        idMap[id] = [fn, obj];
        return id;
    }

    public detach(type, fn:Function, obj):number{
        let idMap = this._typeMap[type];
        if(!idMap){
            return;
        }

        for(var id in idMap){
            let [fn_, obj_] = idMap[id];
            if(fn == fn_ && obj == obj_){
                delete idMap[id];
                return Number(id);
            }
        }
    }

    public detachId(type, id:number){
        let idMap = this._typeMap[type];
        if(!idMap){
            return;
        }
        delete idMap[id];
    }

    public notify(type, data?:any){
        let idMap = this._typeMap[type];
        if(!idMap){
            return;
        }
        for (var id in idMap){
            let [fn_, obj_] = idMap[id];
            if((fn_ as Function).call(obj_, data)){
                return true;
            }
        }
        return false;

    }


    private static _E:DEvent = new DEvent();
    public static attach(type, fn:Function, obj:any):number{
        return this._E.attach(type, fn, obj);
    }

    public static detach(type, fn:Function, obj):number{
        return this._E.detach(type, fn, obj);
    }

    public static detachId(type, id:number){
        this._E.detachId(type, id);
    }

    public static notify(type, data?:any){
        this._E.notify(type, data);
    }
}
